// @tag core
// @define Ext.Boot
var Ext = Ext || {};
//<editor-fold desc="Boot">
/**
 * @class Ext.Boot
 * @singleton
 * @private
 */
Ext.Boot = Ext.Boot || (function(emptyFn) {
  var doc = document,
    _emptyArray = [],
    _config = {
      /**
       * @cfg {Boolean} [disableCaching=true]
       * If `true` current timestamp is added to script URL's to prevent caching.
       * In debug builds, adding a "cache" or "disableCacheBuster" query parameter
       * to the page's URL will set this to `false`.
       */
      disableCaching : (/[?&](?:cache|disableCacheBuster)\b/i.test(location.search)
          || !(/http[s]?\:/i.test(location.href)) || /(^|[ ;])ext-cache=1/.test(doc.cookie)) ? false : true,
      /**
       * @cfg {String} [disableCachingParam="_dc"]
       * The query parameter name for the cache buster's timestamp.
       */
      disableCachingParam : '_dc',
      /**
       * @cfg {Boolean} loadDelay
       * Millisecond delay between asynchronous script injection (prevents stack
       * overflow on some user agents) 'false' disables delay but potentially
       * increases stack load.
       */
      loadDelay : false,
      /**
       * @cfg {Boolean} preserveScripts
       * `false` to remove asynchronously loaded scripts, `true` to retain script
       * element for browser debugger compatibility and improved load performance.
       */
      preserveScripts : true,
      /**
       * @cfg {String} [charset=UTF-8]
       * Optional charset to specify encoding of dynamic content.
       */
      charset : 'UTF-8'
    },
    _assetConfig = {},
    cssRe = /\.css(?:\?|$)/i,
    resolverEl = doc.createElement('a'),
    isBrowser = typeof window !== 'undefined',
    _environment = {
      browser : isBrowser,
      node : !isBrowser && (typeof require === 'function'),
      phantom : (window && (window._phantom || window.callPhantom)) || /PhantomJS/.test(window.navigator.userAgent)
    },
    _tags = (Ext.platformTags = {}),
    //<debug>
    // All calls to _debug are commented out to speed up old browsers a bit;
    // yes that makes a difference because the cost of concatenating strings
    // and passing them into _debug() adds up pretty quickly.
    _debug = function(message) {
      //console.log(message);
    },
    //</debug>
    _apply = function(object, config, defaults) {
      if (defaults) {
        _apply(object, defaults);
      }
      if (object && config && typeof config === 'object') {
        for (var i in config) {
          object[i] = config[i];
        }
      }
      return object;
    },
    _merge = function() {
      var lowerCase = false,
        obj = Array.prototype.shift.call(arguments), index, i, len, value;
      if (typeof arguments[arguments.length - 1] === 'boolean') {
        lowerCase = Array.prototype.pop.call(arguments);
      }
      len = arguments.length;
      for (index = 0; index < len; index++) {
        value = arguments[index];
        if (typeof value === 'object') {
          for (i in value) {
            obj[lowerCase ? i.toLowerCase() : i] = value[i];
          }
        }
      }
      return obj;
    },
    _getKeys = (typeof Object.keys == 'function') ? function(object) {
      if (!object) { return []; }
      return Object.keys(object);
    } : function(object) {
      var keys = [], property;
      for (property in object) {
        if (object.hasOwnProperty(property)) {
          keys.push(property);
        }
      }
      return keys;
    },
    /*
     * The Boot loader class manages Request objects that contain one or more
     * individual urls that need to be loaded. Requests can be performed
     * synchronously or asynchronously, but will always evaluate urls in the
     * order specified on the request object.
     */
    Boot = {
      loading : 0,
      loaded : 0,
      apply : _apply,
      env : _environment,
      config : _config,
      /**
       * @cfg {Object} assetConfig
       * A map (url->assetConfig) that contains information about assets loaded by the Microlaoder.
       */
      assetConfig : _assetConfig,
      // Keyed by absolute URL this object holds "true" if that URL is already loaded
      // or an array of callbacks to call once it loads.
      scripts : {
      /*
       * Entry objects 'http://foo.com/bar/baz/Thing.js': { done: true, el:
       * scriptEl || linkEl, preserve: true, requests: [ request1, ... ] }
       */
      },
      /**
       * contains the current script name being loaded
       * (loadSync or sequential load only)
       */
      currentFile : null,
      suspendedQueue : [],
      currentRequest : null,
      // when loadSync is called, need to cause subsequent load requests to also be loadSync,
      // eg, when Ext.require(...) is called
      syncMode : false,
      /*
       * simple helper method for debugging
       */
      //<debug>
      debug : _debug,
      //</debug>
      /**
       * enables / disables loading scripts via script / link elements rather
       * than using ajax / eval
       */
      useElements : true,
      listeners : [],
      Request : Request,
      Entry : Entry,
      allowMultipleBrowsers : false,
      browserNames : {
        ie : 'IE',
        firefox : 'Firefox',
        safari : 'Safari',
        chrome : 'Chrome',
        opera : 'Opera',
        dolfin : 'Dolfin',
        edge : 'Edge',
        webosbrowser : 'webOSBrowser',
        chromeMobile : 'ChromeMobile',
        chromeiOS : 'ChromeiOS',
        silk : 'Silk',
        other : 'Other'
      },
      osNames : {
        ios : 'iOS',
        android : 'Android',
        windowsPhone : 'WindowsPhone',
        webos : 'webOS',
        blackberry : 'BlackBerry',
        rimTablet : 'RIMTablet',
        mac : 'MacOS',
        win : 'Windows',
        tizen : 'Tizen',
        linux : 'Linux',
        bada : 'Bada',
        chromeOS : 'ChromeOS',
        other : 'Other'
      },
      browserPrefixes : {
        ie : 'MSIE ',
        edge : 'Edge/',
        firefox : 'Firefox/',
        chrome : 'Chrome/',
        safari : 'Version/',
        opera : 'OPR/',
        dolfin : 'Dolfin/',
        webosbrowser : 'wOSBrowser/',
        chromeMobile : 'CrMo/',
        chromeiOS : 'CriOS/',
        silk : 'Silk/'
      },
      // When a UA reports multiple browsers this list is used to prioritize the 'real' browser
      // lower index number will win
      browserPriority : ['edge', 'opera', 'dolfin', 'webosbrowser', 'silk', 'chromeiOS', 'chromeMobile', 'ie',
          'firefox', 'safari', 'chrome'],
      osPrefixes : {
        tizen : '(Tizen )',
        ios : 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',
        android : '(Android |HTC_|Silk/)', // Some HTC devices ship with an OSX userAgent by default,
        // so we need to add a direct check for HTC_
        windowsPhone : 'Windows Phone ',
        blackberry : '(?:BlackBerry|BB)(?:.*)Version\/',
        rimTablet : 'RIM Tablet OS ',
        webos : '(?:webOS|hpwOS)\/',
        bada : 'Bada\/',
        chromeOS : 'CrOS '
      },
      fallbackOSPrefixes : {
        windows : 'win',
        mac : 'mac',
        linux : 'linux'
      },
      devicePrefixes : {
        iPhone : 'iPhone',
        iPod : 'iPod',
        iPad : 'iPad'
      },
      maxIEVersion : 12,
      /**
       * The default function that detects various platforms and sets tags
       * in the platform map accordingly.  Examples are iOS, android, tablet, etc.
       * @param tags the set of tags to populate
       */
      detectPlatformTags : function() {
        var me = this,
          ua = navigator.userAgent,
          isMobile = /Mobile(\/|\s)/.test(ua),
          element = document.createElement('div'),
          isEventSupported = function(name, tag) {
            if (tag === undefined) {
              tag = window;
            }
            var eventName = 'on' + name.toLowerCase(),
              isSupported = (eventName in element);
            if (!isSupported) {
              if (element.setAttribute && element.removeAttribute) {
                element.setAttribute(eventName, '');
                isSupported = typeof element[eventName] === 'function';
                if (typeof element[eventName] !== 'undefined') {
                  element[eventName] = undefined;
                }
                element.removeAttribute(eventName);
              }
            }
            return isSupported;
          },
          // Browser Detection
          getBrowsers = function() {
            var browsers = {}, maxIEVersion, prefix, value, key, index, len, match, version, matched;
            // MS Edge browser (and possibly others) can report multiple browsers in the UserAgent
            // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
            // we use this to prioritize the actual browser in this situation
            len = me.browserPriority.length;
            for (index = 0; index < len; index++) {
              key = me.browserPriority[index];
              if (!matched) {
                value = me.browserPrefixes[key];
                match = ua.match(new RegExp('(' + value + ')([\\w\\._]+)'));
                version = match && match.length > 1 ? parseInt(match[2]) : 0;
                if (version) {
                  matched = true;
                }
              } else {
                version = 0;
              }
              browsers[key] = version;
            }
            //Deal with IE document mode
            if (browsers.ie) {
              var mode = document.documentMode;
              if (mode >= 8) {
                browsers.ie = mode;
              }
            }
            // Fancy IE greater than and less then quick tags
            version = browsers.ie || false;
            maxIEVersion = Math.max(version, me.maxIEVersion);
            for (index = 8; index <= maxIEVersion; ++index) {
              prefix = 'ie' + index;
              browsers[prefix + 'm'] = version ? version <= index : 0;
              browsers[prefix] = version ? version === index : 0;
              browsers[prefix + 'p'] = version ? version >= index : 0;
            }
            return browsers;
          },
          //OS Detection
          getOperatingSystems = function() {
            var systems = {}, value, key, keys, index, len, match, matched, version, activeCount;
            keys = _getKeys(me.osPrefixes);
            len = keys.length;
            for (index = 0, activeCount = 0; index < len; index++) {
              key = keys[index];
              value = me.osPrefixes[key];
              match = ua.match(new RegExp('(' + value + ')([^\\s;]+)'));
              matched = match ? match[1] : null;
              // This is here because some HTC android devices show an OSX Snow Leopard userAgent by default.
              // And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent
              if (matched && (matched === 'HTC_' || matched === 'Silk/')) {
                version = 2.3;
              } else {
                version = match && match.length > 1 ? parseFloat(match[match.length - 1]) : 0;
              }
              if (version) {
                activeCount++;
              }
              systems[key] = version;
            }
            keys = _getKeys(me.fallbackOSPrefixes);
            // If no OS could be found we resort to the fallbacks, otherwise we just
            // falsify the fallbacks
            len = keys.length;
            for (index = 0; index < len; index++) {
              key = keys[index];
              // No OS was detected from osPrefixes
              if (activeCount === 0) {
                value = me.fallbackOSPrefixes[key];
                match = ua.toLowerCase().match(new RegExp(value));
                systems[key] = match ? true : 0;
              } else {
                systems[key] = 0;
              }
            }
            return systems;
          },
          // Device Detection
          getDevices = function() {
            var devices = {}, value, key, keys, index, len, match;
            keys = _getKeys(me.devicePrefixes);
            len = keys.length;
            for (index = 0; index < len; index++) {
              key = keys[index];
              value = me.devicePrefixes[key];
              match = ua.match(new RegExp(value));
              devices[key] = match ? true : 0;
            }
            return devices;
          },
          browsers = getBrowsers(),
          systems = getOperatingSystems(),
          devices = getDevices(),
          platformParams = Boot.loadPlatformsParam();
        // We apply platformParams from the query here first to allow for forced user valued
        // to be used in calculation of generated tags
        _merge(_tags, browsers, systems, devices, platformParams, true);
        _tags.phone = !!((_tags.iphone || _tags.ipod)
            || (!_tags.silk && (_tags.android && (_tags.android < 3 || isMobile))) || (_tags.blackberry && isMobile) || (_tags.windowsphone));
        _tags.tablet = !!(!_tags.phone && (_tags.ipad || _tags.android || _tags.silk || _tags.rimtablet || (_tags.ie10 && /; Touch/
          .test(ua))));
        _tags.touch =
        // if the browser has touch events we can be reasonably sure the device has
        // a touch screen
        isEventSupported('touchend') ||
            // browsers that use pointer event have maxTouchPoints > 0 if the
            // device supports touch input
            // http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints
            navigator.maxTouchPoints ||
            // IE10 uses a vendor-prefixed maxTouchPoints property
            navigator.msMaxTouchPoints;
        _tags.desktop = !_tags.phone && !_tags.tablet;
        _tags.cordova = _tags.phonegap = !!(window.PhoneGap || window.Cordova || window.cordova);
        _tags.webview = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(ua);
        _tags.androidstock = (_tags.android <= 4.3) && (_tags.safari || _tags.silk);
        // Re-apply any query params here to allow for user override of generated tags (desktop, touch, tablet, etc)
        _merge(_tags, platformParams, true);
      },
      /**
       * Extracts user supplied platform tags from the "platformTags" query parameter
       * of the form:
       *
       *      ?platformTags=name:state,name:state,...
       *
       * (each tag defaults to true when state is unspecified)
       *
       * Example:
       *
       *      ?platformTags=isTablet,isPhone:false,isDesktop:0,iOS:1,Safari:true, ...
       *
       * @returns {Object} the platform tags supplied by the query string
       */
      loadPlatformsParam : function() {
        // Check if the ?platform parameter is set in the URL
        var paramsString = window.location.search.substr(1),
          paramsArray = paramsString.split("&"),
          params = {}, i,
          platforms = {}, tmpArray, tmplen, platform, name, enabled;
        for (i = 0; i < paramsArray.length; i++) {
          tmpArray = paramsArray[i].split("=");
          params[tmpArray[0]] = tmpArray[1];
        }
        if (params.platformTags) {
          tmpArray = params.platformTags.split(",");
          for (tmplen = tmpArray.length, i = 0; i < tmplen; i++) {
            platform = tmpArray[i].split(":");
            name = platform[0];
            enabled = true;
            if (platform.length > 1) {
              enabled = platform[1];
              if (enabled === 'false' || enabled === '0') {
                enabled = false;
              }
            }
            platforms[name] = enabled;
          }
        }
        return platforms;
      },
      filterPlatform : function(platform, excludes) {
        platform = _emptyArray.concat(platform || _emptyArray);
        excludes = _emptyArray.concat(excludes || _emptyArray);
        var plen = platform.length,
          elen = excludes.length,
          include = (!plen && elen), // default true if only excludes specified
          i, tag;
        for (i = 0; i < plen && !include; i++) {
          tag = platform[i];
          include = !!_tags[tag];
        }
        for (i = 0; i < elen && include; i++) {
          tag = excludes[i];
          include = !_tags[tag];
        }
        return include;
      },
      init : function() {
        var scriptEls = doc.getElementsByTagName('script'),
          script = scriptEls[0],
          len = scriptEls.length,
          re = /\/ext(\-[a-z\-]+)?\.js$/, entry, src, state, baseUrl, key, n, origin;
        // No check for script definedness because there always should be at least one
        Boot.hasReadyState = ("readyState" in script);
        Boot.hasAsync = ("async" in script);
        Boot.hasDefer = ("defer" in script);
        Boot.hasOnLoad = ("onload" in script);
        // Feature detecting IE
        Boot.isIE8 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && !Boot.hasOnLoad;
        Boot.isIE9 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
        Boot.isIE10p = Boot.hasReadyState && Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
        Boot.isIE10 = (new Function('/*@cc_on return @_jscript_version @*/')()) === 10;
        Boot.isIE10m = Boot.isIE10 || Boot.isIE9 || Boot.isIE8;
        // IE11 does not support conditional compilation so we detect it by exclusion
        Boot.isIE11 = Boot.isIE10p && !Boot.isIE10;
        // Since we are loading after other scripts, and we needed to gather them
        // anyway, we track them in _scripts so we don't have to ask for them all
        // repeatedly.
        for (n = 0; n < len; n++) {
          src = (script = scriptEls[n]).src;
          if (!src) {
            continue;
          }
          state = script.readyState || null;
          // If we find a script file called "ext-*.js", then the base path is that file's base path.
          if (!baseUrl && re.test(src)) {
            baseUrl = src;
          }
          if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {
            //<debug>
            //                         _debug("creating entry " + key + " in Boot.init");
            //</debug>
            entry = new Entry({
              key : key,
              url : src,
              done : state === null || // non-IE
                  state === 'loaded' || state === 'complete', // IE only
              el : script,
              prop : 'src'
            });
          }
        }
        if (!baseUrl) {
          script = scriptEls[scriptEls.length - 1];
          baseUrl = script.src;
        }
        Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
        origin = window.location.origin || window.location.protocol + "//" + window.location.hostname
            + (window.location.port ? ':' + window.location.port : '');
        Boot.origin = origin;
        Boot.detectPlatformTags();
        Ext.filterPlatform = Boot.filterPlatform;
      },
      /**
       * This method returns a canonical URL for the given URL.
       *
       * For example, the following all produce the same canonical URL (which is the
       * last one):
       *
       *      http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js?_dc=12345
       *      http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js
       *      http://foo.com/bar/baz/zoo/derp/../jazz/../../goo/Thing.js
       *      http://foo.com/bar/baz/zoo/../goo/Thing.js
       *      http://foo.com/bar/baz/goo/Thing.js
       *
       * @private
       */
      canonicalUrl : function(url) {
        // *WARNING WARNING WARNING*
        // This method yields the most correct result we can get but it is EXPENSIVE!
        // In ALL browsers! When called multiple times in a sequence, as if when
        // we resolve dependencies for entries, it will cause garbage collection events
        // and overall painful slowness. This is why we try to avoid it as much as we can.
        // 
        // @TODO - see if we need this fallback logic
        // http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
        resolverEl.href = url;
        var ret = resolverEl.href,
          dc = _config.disableCachingParam,
          pos = dc ? ret.indexOf(dc + '=') : -1, c, end;
        // If we have a _dc query parameter we need to remove it from the canonical
        // URL.
        if (pos > 0 && ((c = ret.charAt(pos - 1)) === '?' || c === '&')) {
          end = ret.indexOf('&', pos);
          end = (end < 0) ? '' : ret.substring(end);
          if (end && c === '?') {
            ++pos; // keep the '?'
            end = end.substring(1); // remove the '&'
          }
          ret = ret.substring(0, pos - 1) + end;
        }
        return ret;
      },
      /**
       * Get the config value corresponding to the specified name. If no name is given, will return the config object
       * @param {String} name The config property name
       * @return {Object}
       */
      getConfig : function(name) {
        return name ? Boot.config[name] : Boot.config;
      },
      /**
       * Set the configuration.
       * @param {Object} config The config object to override the default values.
       * @return {Ext.Boot} this
       */
      setConfig : function(name, value) {
        if (typeof name === 'string') {
          Boot.config[name] = value;
        } else {
          for (var s in name) {
            Boot.setConfig(s, name[s]);
          }
        }
        return Boot;
      },
      getHead : function() {
        return Boot.docHead || (Boot.docHead = doc.head || doc.getElementsByTagName('head')[0]);
      },
      create : function(url, key, cfg) {
        var config = cfg || {};
        config.url = url;
        config.key = key;
        return Boot.scripts[key] = new Entry(config);
      },
      getEntry : function(url, cfg, canonicalPath) {
        var key, entry;
        // Canonicalizing URLs via anchor element href yields the most correct result
        // but is *extremely* resource heavy so we need to avoid it whenever possible
        key = canonicalPath ? url : Boot.canonicalUrl(url);
        entry = Boot.scripts[key];
        if (!entry) {
          entry = Boot.create(url, key, cfg);
          if (canonicalPath) {
            entry.canonicalPath = true;
          }
        }
        return entry;
      },
      registerContent : function(url, type, content) {
        var cfg = {
          content : content,
          loaded : true,
          css : type === 'css'
        };
        return Boot.getEntry(url, cfg);
      },
      processRequest : function(request, sync) {
        request.loadEntries(sync);
      },
      load : function(request) {
        //<debug>
        //                 _debug("Boot.load called");
        //</debug>
        var request = new Request(request);
        if (request.sync || Boot.syncMode) { return Boot.loadSync(request); }
        // If there is a request in progress, we must
        // queue this new request to be fired  when the current request completes.
        if (Boot.currentRequest) {
          //<debug>
          //                     _debug("current active request, suspending this request");
          //</debug>
          // trigger assignment of entries now to ensure that overlapping
          // entries with currently running requests will synchronize state
          // with this pending one as they complete
          request.getEntries();
          Boot.suspendedQueue.push(request);
        } else {
          Boot.currentRequest = request;
          Boot.processRequest(request, false);
        }
        return Boot;
      },
      loadSync : function(request) {
        //<debug>
        //                 _debug("Boot.loadSync called");
        //</debug>
        var request = new Request(request);
        Boot.syncMode++;
        Boot.processRequest(request, true);
        Boot.syncMode--;
        return Boot;
      },
      loadBasePrefix : function(request) {
        request = new Request(request);
        request.prependBaseUrl = true;
        return Boot.load(request);
      },
      loadSyncBasePrefix : function(request) {
        request = new Request(request);
        request.prependBaseUrl = true;
        return Boot.loadSync(request);
      },
      requestComplete : function(request) {
        var next;
        if (Boot.currentRequest === request) {
          Boot.currentRequest = null;
          while (Boot.suspendedQueue.length > 0) {
            next = Boot.suspendedQueue.shift();
            if (!next.done) {
              //<debug>
              //                             _debug("resuming suspended request");
              //</debug>
              Boot.load(next);
              break;
            }
          }
        }
        if (!Boot.currentRequest && Boot.suspendedQueue.length == 0) {
          Boot.fireListeners();
        }
      },
      isLoading : function() {
        return !Boot.currentRequest && Boot.suspendedQueue.length == 0;
      },
      fireListeners : function() {
        var listener;
        while (Boot.isLoading() && (listener = Boot.listeners.shift())) {
          listener();
        }
      },
      onBootReady : function(listener) {
        if (!Boot.isLoading()) {
          listener();
        } else {
          Boot.listeners.push(listener);
        }
      },
      /**
       * this is a helper function used by Ext.Loader to flush out
       * 'uses' arrays for classes in some Ext versions
       */
      getPathsFromIndexes : function(indexMap, loadOrder) {
        // In older versions indexMap was an object instead of a sparse array
        if (!('length' in indexMap)) {
          var indexArray = [], index;
          for (index in indexMap) {
            if (!isNaN(+index)) {
              indexArray[+index] = indexMap[index];
            }
          }
          indexMap = indexArray;
        }
        return Request.prototype.getPathsFromIndexes(indexMap, loadOrder);
      },
      createLoadOrderMap : function(loadOrder) {
        return Request.prototype.createLoadOrderMap(loadOrder);
      },
      fetch : function(url, complete, scope, async) {
        async = (async === undefined) ? !!complete : async;
        var xhr = new XMLHttpRequest(), result, status, content,
          exception = false,
          readyStateChange = function() {
            if (xhr && xhr.readyState == 4) {
              status = (xhr.status === 1223) ? 204 : (xhr.status === 0 && ((self.location || {}).protocol === 'file:' || (self.location || {}).protocol === 'ionp:')) ? 200 : xhr.status;
              content = xhr.responseText;
              result = {
                content : content,
                status : status,
                exception : exception
              };
              if (complete) {
                complete.call(scope, result);
              }
              xhr.onreadystatechange = emptyFn;
              xhr = null;
            }
          };
        if (async) {
          xhr.onreadystatechange = readyStateChange;
        }
        try {
          //<debug>
          //                     _debug("fetching " + url + " " + (async ? "async" : "sync"));
          //</debug>
          xhr.open('GET', url, async);
          xhr.send(null);
        } catch (err) {
          exception = err;
          readyStateChange();
          return result;
        }
        if (!async) {
          readyStateChange();
        }
        return result;
      },
      notifyAll : function(entry) {
        entry.notifyRequests();
      }
    };
  function Request(cfg) {
    //The request class encapsulates a series of Entry objects
    //and provides notification around the completion of all Entries
    //in this request.
    if (cfg.$isRequest) { return cfg; }
    var cfg = cfg.url ? cfg : {
      url : cfg
    },
      url = cfg.url,
      urls = url.charAt ? [url] : url,
      charset = cfg.charset || Boot.config.charset;
    _apply(this, cfg);
    delete this.url;
    this.urls = urls;
    this.charset = charset;
  };
  Request.prototype = {
    $isRequest : true,
    createLoadOrderMap : function(loadOrder) {
      var len = loadOrder.length,
        loadOrderMap = {}, i, element;
      for (i = 0; i < len; i++) {
        element = loadOrder[i];
        loadOrderMap[element.path] = element;
      }
      return loadOrderMap;
    },
    getLoadIndexes : function(item, indexMap, loadOrder, includeUses, skipLoaded) {
      var resolved = [],
        queue = [item],
        itemIndex = item.idx, queue, entry, dependencies, depIndex, i, len;
      if (indexMap[itemIndex]) {
        // prevent cycles
        return resolved;
      }
      // Both indexMap and resolved are sparse arrays keyed by indexes.
      // This gives us a naturally sorted sequence of indexes later on
      // when we need to convert them to paths.
      // indexMap is the map of all indexes we have visited at least once
      // per the current expandUrls() invocation, and resolved is the map
      // of all dependencies for the current item that are not included
      // in indexMap.
      indexMap[itemIndex] = resolved[itemIndex] = true;
      while (item = queue.shift()) {
        // Canonicalizing URLs is expensive, we try to avoid it
        if (item.canonicalPath) {
          entry = Boot.getEntry(item.path, null, true);
        } else {
          entry = Boot.getEntry(this.prepareUrl(item.path));
        }
        if (!(skipLoaded && entry.done)) {
          if (includeUses && item.uses && item.uses.length) {
            dependencies = item.requires.concat(item.uses);
          } else {
            dependencies = item.requires;
          }
          for (i = 0, len = dependencies.length; i < len; i++) {
            depIndex = dependencies[i];
            if (!indexMap[depIndex]) {
              indexMap[depIndex] = resolved[depIndex] = true;
              queue.push(loadOrder[depIndex]);
            }
          }
        }
      }
      return resolved;
    },
    getPathsFromIndexes : function(indexes, loadOrder) {
      var paths = [], index, len;
      // indexes is a sparse array with values being true for defined indexes
      for (index = 0, len = indexes.length; index < len; index++) {
        if (indexes[index]) {
          paths.push(loadOrder[index].path);
        }
      }
      return paths;
    },
    expandUrl : function(url, loadOrder, loadOrderMap, indexMap, includeUses, skipLoaded) {
      var item, resolved;
      if (loadOrder) {
        item = loadOrderMap[url];
        if (item) {
          resolved = this.getLoadIndexes(item, indexMap, loadOrder, includeUses, skipLoaded);
          if (resolved.length) { return this.getPathsFromIndexes(resolved, loadOrder); }
        }
      }
      return [url];
    },
    expandUrls : function(urls, includeUses) {
      var me = this,
        loadOrder = me.loadOrder,
        expanded = [],
        expandMap = {},
        indexMap = [], loadOrderMap, tmpExpanded, i, len, t, tlen, tUrl;
      if (typeof urls === "string") {
        urls = [urls];
      }
      if (loadOrder) {
        loadOrderMap = me.loadOrderMap;
        if (!loadOrderMap) {
          loadOrderMap = me.loadOrderMap = me.createLoadOrderMap(loadOrder);
        }
      }
      for (i = 0, len = urls.length; i < len; i++) {
        // We don't want to skip loaded entries (last argument === false).
        // There are some overrides that get loaded before their respective classes,
        // and when the class dependencies are processed we don't want to skip over
        // the overrides' dependencies just because they were loaded first.
        tmpExpanded = this.expandUrl(urls[i], loadOrder, loadOrderMap, indexMap, includeUses, false);
        for (t = 0, tlen = tmpExpanded.length; t < tlen; t++) {
          tUrl = tmpExpanded[t];
          if (!expandMap[tUrl]) {
            expandMap[tUrl] = true;
            expanded.push(tUrl);
          }
        }
      }
      if (expanded.length === 0) {
        expanded = urls;
      }
      return expanded;
    },
    expandLoadOrder : function() {
      var me = this,
        urls = me.urls, expanded;
      if (!me.expanded) {
        expanded = this.expandUrls(urls, true);
        me.expanded = true;
      } else {
        expanded = urls;
      }
      me.urls = expanded;
      // if we added some urls to the request to honor the indicated
      // load order, the request needs to be sequential
      if (urls.length != expanded.length) {
        me.sequential = true;
      }
      return me;
    },
    getUrls : function() {
      this.expandLoadOrder();
      return this.urls;
    },
    prepareUrl : function(url) {
      if (this.prependBaseUrl) { return Boot.baseUrl + url; }
      return url;
    },
    getEntries : function() {
      var me = this,
        entries = me.entries, loadOrderMap, item, i, entry, urls, url;
      if (!entries) {
        entries = [];
        urls = me.getUrls();
        // If we have loadOrder array then the map will be expanded by now
        if (me.loadOrder) {
          loadOrderMap = me.loadOrderMap;
        }
        for (i = 0; i < urls.length; i++) {
          url = me.prepareUrl(urls[i]);
          if (loadOrderMap) {
            item = loadOrderMap[url];
          }
          entry = Boot.getEntry(url, {
            buster : me.buster,
            charset : me.charset
          }, item && item.canonicalPath);
          entry.requests.push(me);
          entries.push(entry);
        }
        me.entries = entries;
      }
      return entries;
    },
    loadEntries : function(sync) {
      var me = this,
        entries = me.getEntries(),
        len = entries.length,
        start = me.loadStart || 0, continueLoad, entries, entry, i;
      if (sync !== undefined) {
        me.sync = sync;
      }
      me.loaded = me.loaded || 0;
      me.loading = me.loading || len;
      for (i = start; i < len; i++) {
        entry = entries[i];
        if (!entry.loaded) {
          continueLoad = entries[i].load(me.sync);
        } else {
          continueLoad = true;
        }
        if (!continueLoad) {
          me.loadStart = i;
          entry.onDone(function() {
            me.loadEntries(sync);
          });
          break;
        }
      }
      me.processLoadedEntries();
    },
    processLoadedEntries : function() {
      var me = this,
        entries = me.getEntries(),
        len = entries.length,
        start = me.startIndex || 0, i, entry;
      if (!me.done) {
        for (i = start; i < len; i++) {
          entry = entries[i];
          if (!entry.loaded) {
            me.startIndex = i;
            return;
          }
          if (!entry.evaluated) {
            entry.evaluate();
          }
          if (entry.error) {
            me.error = true;
          }
        }
        me.notify();
      }
    },
    notify : function() {
      var me = this;
      if (!me.done) {
        var error = me.error,
          fn = me[error ? 'failure' : 'success'],
          delay = ('delay' in me) ? me.delay : (error ? 1 : Boot.config.chainDelay),
          scope = me.scope || me;
        me.done = true;
        if (fn) {
          if (delay === 0 || delay > 0) {
            // Free the stack (and defer the next script)
            setTimeout(function() {
              fn.call(scope, me);
            }, delay);
          } else {
            fn.call(scope, me);
          }
        }
        me.fireListeners();
        Boot.requestComplete(me);
      }
    },
    onDone : function(listener) {
      var me = this,
        listeners = me.listeners || (me.listeners = []);
      if (me.done) {
        listener(me);
      } else {
        listeners.push(listener);
      }
    },
    fireListeners : function() {
      var listeners = this.listeners, listener;
      if (listeners) {
        //<debug>
        //                 _debug("firing request listeners");
        //</debug>
        while ((listener = listeners.shift())) {
          listener(this);
        }
      }
    }
  };
  function Entry(cfg) {
    //The Entry class is a token to manage the load and evaluation
    //state of a particular url.  It is used to notify all Requests
    //interested in this url that the content is available.
    if (cfg.$isEntry) { return cfg; }
    //<debug>
    //         _debug("creating entry for " + cfg.url);
    //</debug>
    var charset = cfg.charset || Boot.config.charset,
      manifest = Ext.manifest,
      loader = manifest && manifest.loader,
      cache = (cfg.cache !== undefined) ? cfg.cache : (loader && loader.cache), buster, busterParam;
    if (Boot.config.disableCaching) {
      if (cache === undefined) {
        cache = !Boot.config.disableCaching;
      }
      if (cache === false) {
        buster = +new Date();
      } else if (cache !== true) {
        buster = cache;
      }
      if (buster) {
        busterParam = (loader && loader.cacheParam) || Boot.config.disableCachingParam;
        buster = busterParam + "=" + buster;
      }
    }
    _apply(this, cfg);
    this.charset = charset;
    this.buster = buster;
    this.requests = [];
  };
  Entry.prototype = {
    $isEntry : true,
    done : false,
    evaluated : false,
    loaded : false,
    isCrossDomain : function() {
      var me = this;
      if (me.crossDomain === undefined) {
        //<debug>
        //                 _debug("checking " + me.getLoadUrl() + " for prefix " + Boot.origin);
        //</debug>
        me.crossDomain = (me.getLoadUrl().indexOf(Boot.origin) !== 0);
      }
      return me.crossDomain;
    },
    isCss : function() {
      var me = this;
      if (me.css === undefined) {
        if (me.url) {
          var assetConfig = Boot.assetConfig[me.url];
          me.css = assetConfig ? assetConfig.type === "css" : cssRe.test(me.url);
        } else {
          me.css = false;
        }
      }
      return this.css;
    },
    getElement : function(tag) {
      var me = this,
        el = me.el;
      if (!el) {
        //<debug>
        //                 _debug("creating element for " + me.url);
        //</debug>
        if (me.isCss()) {
          tag = tag || "link";
          el = doc.createElement(tag);
          if (tag == "link") {
            el.rel = 'stylesheet';
            me.prop = 'href';
          } else {
            me.prop = "textContent";
          }
          el.type = "text/css";
        } else {
          tag = tag || "script";
          el = doc.createElement(tag);
          el.type = 'text/javascript';
          me.prop = 'src';
          if (me.charset) {
            el.charset = me.charset;
          }
          if (Boot.hasAsync) {
            el.async = false;
          }
        }
        me.el = el;
      }
      return el;
    },
    getLoadUrl : function() {
      var me = this, url;
      url = me.canonicalPath ? me.url : Boot.canonicalUrl(me.url);
      if (!me.loadUrl) {
        me.loadUrl = !!me.buster ? (url + (url.indexOf('?') === -1 ? '?' : '&') + me.buster) : url;
      }
      return me.loadUrl;
    },
    fetch : function(req) {
      var url = this.getLoadUrl(),
        async = !!req.async,
        complete = req.complete;
      Boot.fetch(url, complete, this, async);
    },
    onContentLoaded : function(response) {
      var me = this,
        status = response.status,
        content = response.content,
        exception = response.exception,
        url = this.getLoadUrl();
      me.loaded = true;
      if ((exception || status === 0) && !_environment.phantom) {
        me.error =
        //<debug>
        ("Failed loading synchronously via XHR: '" + url
            + "'. It's likely that the file is either being loaded from a "
            + "different domain or from the local file system where cross "
            + "origin requests are not allowed for security reasons. Try " + "asynchronous loading instead.") ||
        //</debug>
        true;
        me.evaluated = true;
      } else if ((status >= 200 && status < 300) || status === 304 || _environment.phantom
          || (status === 0 && content.length > 0)) {
        me.content = content;
      } else {
        me.error =
        //<debug>
        ("Failed loading synchronously via XHR: '" + url + "'. Please verify that the file exists. XHR status code: " + status) ||
        //</debug>
        true;
        me.evaluated = true;
      }
    },
    createLoadElement : function(callback) {
      var me = this,
        el = me.getElement();
      me.preserve = true;
      el.onerror = function() {
        me.error = true;
        if (callback) {
          callback();
          callback = null;
        }
      };
      if (Boot.isIE10m) {
        el.onreadystatechange = function() {
          if (this.readyState === 'loaded' || this.readyState === 'complete') {
            if (callback) {
              callback();
              callback = this.onreadystatechange = this.onerror = null;
            }
          }
        };
      } else {
        el.onload = function() {
          callback();
          callback = this.onload = this.onerror = null;
        };
      }
      // IE starts loading here
      el[me.prop] = me.getLoadUrl();
    },
    onLoadElementReady : function() {
      Boot.getHead().appendChild(this.getElement());
      this.evaluated = true;
    },
    inject : function(content, asset) {
      //<debug>
      //             _debug("injecting content for " + this.url);
      //</debug>
      var me = this,
        head = Boot.getHead(),
        url = me.url,
        key = me.key, base, el, ieMode, basePath;
      if (me.isCss()) {
        me.preserve = true;
        basePath = key.substring(0, key.lastIndexOf("/") + 1);
        base = doc.createElement('base');
        base.href = basePath;
        if (head.firstChild) {
          head.insertBefore(base, head.firstChild);
        } else {
          head.appendChild(base);
        }
        // reset the href attribute to cuase IE to pick up the change
        base.href = base.href;
        if (url) {
          content += "\n/*# sourceURL=" + key + " */";
        }
        // create element after setting base
        el = me.getElement("style");
        ieMode = ('styleSheet' in el);
        head.appendChild(base);
        if (ieMode) {
          head.appendChild(el);
          el.styleSheet.cssText = content;
        } else {
          el.textContent = content;
          head.appendChild(el);
        }
        head.removeChild(base);
      } else {
        // Debugger friendly, file names are still shown even though they're
        // eval'ed code. Breakpoints work on both Firebug and Chrome's Web
        // Inspector.
        if (url) {
          content += "\n//# sourceURL=" + key;
        }
        Ext.globalEval(content);
      }
      return me;
    },
    loadCrossDomain : function() {
      var me = this,
        complete = function() {
          me.el.onerror = me.el.onload = emptyFn;
          me.el = null;
          me.loaded = me.evaluated = me.done = true;
          me.notifyRequests();
        };
      me.createLoadElement(function() {
        complete();
      });
      me.evaluateLoadElement();
      // at this point, we need sequential evaluation,
      // which means we can't advance the load until
      // this entry has fully completed
      return false;
    },
    loadElement : function() {
      var me = this,
        complete = function() {
          me.el.onerror = me.el.onload = emptyFn;
          me.el = null;
          me.loaded = me.evaluated = me.done = true;
          me.notifyRequests();
        };
      me.createLoadElement(function() {
        complete();
      });
      me.evaluateLoadElement();
      return true;
    },
    loadSync : function() {
      var me = this;
      me.fetch({
        async : false,
        complete : function(response) {
          me.onContentLoaded(response);
        }
      });
      me.evaluate();
      me.notifyRequests();
    },
    load : function(sync) {
      var me = this;
      if (!me.loaded) {
        if (me.loading) {
          // if we're calling back through load and we're loading but haven't
          // yet loaded, then we should be in a sequential, cross domain
          // load scenario which means we can't continue the load on the
          // request until this entry has fully evaluated, which will mean
          // loaded = evaluated = done = true in one step.  For css files, this
          // will happen immediately upon <link> element creation / insertion,
          // but <script> elements will set this upon load notification
          return false;
        }
        me.loading = true;
        // for async modes, we have some options
        if (!sync) {
          // if cross domain, just inject the script tag and let the onload
          // events drive the progression.
          // IE10 also needs sequential loading because of a bug that makes it
          // fire readystate event prematurely:
          // https://connect.microsoft.com/IE/feedback/details/729164/ie10-dynamic-script-element-fires-loaded-readystate-prematurely
          if (Boot.isIE10 || me.isCrossDomain()) {
            return me.loadCrossDomain();
          }
          // for IE, use the readyStateChange allows us to load scripts in parallel
          // but serialize the evaluation by appending the script node to the
          // document
          else if (!me.isCss() && Boot.hasReadyState) {
            me.createLoadElement(function() {
              me.loaded = true;
              me.notifyRequests();
            });
          } else if (Boot.useElements &&
              // older webkit, phantomjs included, won't fire load for link elements
              !(me.isCss() && _environment.phantom)) {
            return me.loadElement();
          }
          // for other browsers, just ajax the content down in parallel, and use
          // globalEval to serialize evaluation
          else {
            me.fetch({
              async : !sync,
              complete : function(response) {
                me.onContentLoaded(response);
                me.notifyRequests();
              }
            });
          }
        }
        // for sync mode in js, global eval FTW.  IE won't honor the comment
        // paths in the debugger, so eventually we need a sync mode for IE that
        // uses the readyStateChange mechanism
        else {
          me.loadSync();
        }
      }
      // signal that the load process can continue
      return true;
    },
    evaluateContent : function() {
      this.inject(this.content);
      this.content = null;
    },
    evaluateLoadElement : function() {
      Boot.getHead().appendChild(this.getElement());
    },
    evaluate : function() {
      var me = this;
      if (!me.evaluated) {
        if (me.evaluating) { return; }
        me.evaluating = true;
        if (me.content !== undefined) {
          me.evaluateContent();
        } else if (!me.error) {
          me.evaluateLoadElement();
        }
        me.evaluated = me.done = true;
        me.cleanup();
      }
    },
    cleanup : function() {
      var me = this,
        el = me.el, prop;
      if (!el) { return; }
      if (!me.preserve) {
        me.el = null;
        el.parentNode.removeChild(el); // Remove, since its useless now
        for (prop in el) {
          try {
            if (prop !== me.prop) {
              // If we set the src property to null IE
              // will try and request a script at './null'
              el[prop] = null;
            }
            delete el[prop]; // and prepare for GC
          } catch (cleanEx) {
            //ignore
          }
        }
      }
      // Setting to null can cause exceptions if IE ever needs to call these
      // again (like onreadystatechange). This emptyFn has nothing locked in
      // closure scope so it is about as safe as null for memory leaks.
      el.onload = el.onerror = el.onreadystatechange = emptyFn;
    },
    notifyRequests : function() {
      var requests = this.requests,
        len = requests.length, i, request;
      for (i = 0; i < len; i++) {
        request = requests[i];
        request.processLoadedEntries();
      }
      if (this.done) {
        this.fireListeners();
      }
    },
    onDone : function(listener) {
      var me = this,
        listeners = me.listeners || (me.listeners = []);
      if (me.done) {
        listener(me);
      } else {
        listeners.push(listener);
      }
    },
    fireListeners : function() {
      var listeners = this.listeners, listener;
      if (listeners && listeners.length > 0) {
        //<debug>
        //                 _debug("firing event listeners for url " + this.url);
        //</debug>
        while ((listener = listeners.shift())) {
          listener(this);
        }
      }
    }
  };
  /**
   * Turns on or off the "cache buster" applied to dynamically loaded scripts. Normally
   * dynamically loaded scripts have an extra query parameter appended to avoid stale
   * cached scripts. This method can be used to disable this mechanism, and is primarily
   * useful for testing. This is done using a cookie.
   * @param {Boolean} disable True to disable the cache buster.
   * @param {String} [path="/"] An optional path to scope the cookie.
   */
  Ext.disableCacheBuster = function(disable, path) {
    var date = new Date();
    date.setTime(date.getTime() + (disable ? 10 * 365 : -1) * 24 * 60 * 60 * 1000);
    date = date.toGMTString();
    doc.cookie = 'ext-cache=1; expires=' + date + '; path=' + (path || '/');
  };
  //<if nonBrowser>
  if (_environment.node) {
    Boot.prototype.load = Boot.prototype.loadSync = function(request) {
      // @TODO
      require(filePath);
      onLoad.call(scope);
    };
    Boot.prototype.init = emptyFn;
  }
  //</if>
  Boot.init();
  return Boot;
    // NOTE: We run the eval at global scope to protect the body of the function and allow
    // compressors to still process it.
}(function() {
}));//(eval("/*@cc_on!@*/!1"));
/**
 * This method evaluates the given code free of any local variable. This
 * will be at global scope, in others it will be in a function.
 * @param {String} code The code to evaluate.
 * @private
 * @method
 * @member Ext
 */
Ext.globalEval = Ext.globalEval || (this.execScript ? function(code) {
  execScript(code);
} : function($$code) {
  eval.call(window, $$code);
});
//<feature legacyBrowser>
/*
 * Only IE8 & IE/Quirks lack Function.prototype.bind so we polyfill that here.
 */
if (!Function.prototype.bind) {
  (function() {
    var slice = Array.prototype.slice,
      // To reduce overhead on call of the bound fn we have two flavors based on
      // whether we have args to prepend or not:
      bind = function(me) {
        var args = slice.call(arguments, 1),
          method = this;
        if (args.length) { return function() {
          var t = arguments;
          // avoid the slice/concat if the caller does not supply args
          return method.apply(me, t.length ? args.concat(slice.call(t)) : args);
        }; }
        // this is the majority use case - just fn.bind(this) and no args
        args = null;
        return function() {
          return method.apply(me, arguments);
        };
      };
    Function.prototype.bind = bind;
    bind.$extjs = true; // to detect this polyfill if one want to improve it
  }());
}
//</feature>
//</editor-fold>
Ext.setResourcePath = function(poolName, path) {
  var manifest = Ext.manifest || (Ext.manifest = {}),
    paths = manifest.resources || (manifest.resources = {});
  if (manifest) {
    if (typeof poolName !== 'string') {
      Ext.apply(paths, poolName);
    } else {
      paths[poolName] = path;
    }
    manifest.resources = paths;
  }
};
Ext.getResourcePath = function(path, poolName, packageName) {
  if (typeof path !== 'string') {
    poolName = path.pool;
    packageName = path.packageName;
    path = path.path;
  }
  var manifest = Ext.manifest,
    paths = manifest && manifest.resources,
    poolPath = paths[poolName],
    output = [];
  if (poolPath == null) {
    poolPath = paths.path;
    if (poolPath == null) {
      poolPath = 'resources';
    }
  }
  if (poolPath) {
    output.push(poolPath);
  }
  if (packageName) {
    output.push(packageName);
  }
  output.push(path);
  return output.join('/');
};